package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"gorm.io/gorm"
	"net/http"
	"regexp"
	"user-center/src/common"
	"user-center/src/constant"
	"user-center/src/db"
	"user-center/src/model"
	"user-center/src/model/request"
	"user-center/src/model/response"
	"user-center/src/utils"
)

func Register(req *request.UserRegisterRequest, ctx *gin.Context) *response.BaseResponse {
	rsp := &response.BaseResponse{}
	// 1. 校验
	if req.UserAccount == "" || req.UserPassword == "" || req.CheckPassword == "" {
		log.Info("UserAccount or UserPassword or CheckPassword is null")
		rsp.ResponseWithError(common.NULL_ERROR, "输入不能为空")
		return rsp
	}
	if len(req.UserAccount) < 6 || len(req.UserPassword) < 6 || len(req.CheckPassword) < 6 {
		log.Info("UserAccount or UserPassword or CheckPassword is too short")
		rsp.ResponseWithError(common.PARAMS_ERROR, "输入密码不能小于六位")
		return rsp
	}

	// 账号不能包含特殊字符
	_, err := regexp.MatchString("/[`~!@#$%^&*()_\\-+=<>?:\"{}|,./;'\\\\[\\]·~！@#￥%……&*（）——\\-+={}|《》？：“”【】、；‘'，。、]/", req.UserAccount)
	if err != nil {
		log.Infof("UserAccount have not available char:%v", err)
		rsp.ResponseWithError(common.PARAMS_ERROR, "账号不能包含特殊字符")
		return rsp
	}

	// 密码与校验密码
	if req.UserPassword != req.CheckPassword {
		log.Info("UserPassword != CheckPassword")
		rsp.ResponseWithError(common.PARAMS_ERROR, "密码与校验密码不相同")
		return rsp
	}

	// 账号不能重复
	var count int64
	if db.GetMySQL().Model(&model.User{}).Where("userAccount=?", req.UserAccount).Count(&count); count != 0 {
		log.Info("userAccount is already setup")
		rsp.ResponseWithError(common.NULL_ERROR, "账号已被注册")
		return rsp
	}

	// 2. 加密
	encryptPassword := utils.EncryptMd5(req.UserPassword)

	// 3. 插入数据
	user := &model.User{
		UserAccount:  req.UserAccount,
		UserPassword: encryptPassword,
		AvatarUrl:    constant.Avatar,
	}

	if err = db.GetMySQL().Model(&model.User{}).Select("userAccount", "userPassword", "avatarUrl").Create(user).Error; err != nil {
		log.Warningf("Create user fail:%v", err)
		rsp.ResponseWithError(common.SYSTEM_ERROR, "数据库创建用户失败")
		return rsp
	}
	// 清楚之前的cookie
	UserLogOut(ctx)
	rsp.ResponseWithData(user.Id)
	return rsp
}

func UserLogin(req *request.UserLoginRequest, ctx *gin.Context) *response.BaseResponse {
	rsp := &response.BaseResponse{}

	// 1. 校验
	if req.UserAccount == "" || req.UserPassword == "" {
		log.Info("UserAccount or UserPassword is null")
		rsp.ResponseWithError(common.NULL_ERROR, "输入不能为空")
		return rsp
	}
	if len(req.UserAccount) < 6 || len(req.UserPassword) < 6 {
		log.Info("UserAccount or UserPassword is too short")
		rsp.ResponseWithError(common.NULL_ERROR, "账号或密码不能小于六位")
		return rsp
	}

	// 账号不能包含特殊字符
	_, err := regexp.MatchString("/[`~!@#$%^&*()_\\-+=<>?:\"{}|,.\\/;'\\\\[\\]·~！@#￥%……&*（）——\\-+={}|《》？：“”【】、；‘'，。、]/", req.UserAccount)
	if err != nil {
		log.Infof("UserAccount have not available char:%v", err)
		rsp.ResponseWithError(common.NULL_ERROR, "账号不能包含特殊字符")
		return rsp
	}

	// 2. 加密
	encryptPassword := utils.EncryptMd5(req.UserPassword)

	// 查询用户是否存在
	user := &model.User{}
	if err = db.GetMySQL().Model(&model.User{}).Where("isDelete=0 and userAccount=? and userPassword=?", req.UserAccount, encryptPassword).First(&user).Error; err != nil {
		log.Warningf("Cannot find the user")
		rsp.ResponseWithError(common.FORBIDDEN, "查询不到用户信息")
		return rsp
	}
	setCookie(ctx, user)
	rsp.ResponseWithData(utils.GetSafetyUser(user))
	return rsp
}

func setCookie(ctx *gin.Context, user *model.User) bool {
	safeUser := utils.GetSafetyUser(user)
	// 使用用户信息设置了 session
	UserInfo, err := json.Marshal(safeUser)
	if err != nil {
		log.Errorf("Json marshal error:%v", err)
		return false
	}
	session := sessions.Default(ctx)
	session.Delete(constant.SessionKey)
	session.Set(constant.SessionKey, UserInfo)
	err = session.Save() //保存 session
	if err != nil {
		return false
	}
	return true
}

func GetUserCurrentInfo(ctx *gin.Context) *model.User {
	session := sessions.Default(ctx)
	userInfo := session.Get(constant.SessionKey)
	if userInfo == nil {
		log.Errorf("没有 session ")
		return nil
	}
	bytesData, ok := userInfo.([]byte)
	if !ok {
		log.Errorf("userInfo.([]byte)  fail")
		return nil
	}
	user := &model.User{}
	if err := json.Unmarshal(bytesData, user); err != nil {
		log.Errorf("json.Unmarshal information fail:%v", err)
		return nil
	}
	return user
}

func GetUserCurrent(ctx *gin.Context) *response.BaseResponse {
	rsp := &response.BaseResponse{}

	user := GetUserCurrentInfo(ctx)
	if user == nil {
		rsp.ResponseWithError(common.NOT_LOGIN, "未登录")
		return rsp
	}
	safetyUser := utils.GetSafetyUser(user)
	rsp.ResponseWithData(safetyUser)
	return rsp
}

func UserLogOut(ctx *gin.Context) {
	ctx.SetCookie(constant.SessionKey, "", -1, "/", "", false, true)
}

func UserInfo(ctx *gin.Context, userId int64) *response.BaseResponse {
	res := &response.BaseResponse{}
	if userId <= 0 {
		res.ResponseWithError(common.PARAMS_ERROR, "参数错误")
		return res
	}

	var userInfo model.User
	if err := db.GetMySQL().Where("id = ?", userId).First(&userInfo).Error; err != nil {
		res.ResponseWithError(common.NULL_ERROR, "没这个人")
		return res
	}

	userInfo = utils.GetSafetyUser(&userInfo)
	res.ResponseWithData(userInfo)
	return res

}
func UserSearch(username string, ctx *gin.Context) *response.BaseResponse {
	res := &response.BaseResponse{}
	if !IsAdmin(ctx) {
		res.ResponseWithError(common.NO_AUTH, "无权限")
		ctx.JSON(http.StatusOK, res)
		return res
	}

	var userList []model.User
	if err := db.GetMySQL().Model(&model.User{}).Where("isDelete=0 and username like ? or username is null", "%"+username+"%").Find(&userList).Error; err != nil {
		log.Warningf("Serch user information fail:%v", err)
		res.ResponseWithError(common.SYSTEM_ERROR, "查询用户信息出错")
		return res
	}

	var safetyUserList []model.User
	for _, v := range userList {
		safetyUser := utils.GetSafetyUser(&v)
		safetyUserList = append(safetyUserList, safetyUser)
	}
	res.ResponseWithData(safetyUserList)
	return res
}

func SearchUserByTags(ctx *gin.Context, tagNameList []string) *response.BaseResponse {
	res := &response.BaseResponse{}
	if tagNameList == nil {
		res.ResponseWithError(common.PARAMS_ERROR, "无参数")
	}
	var userList []model.User
	for _, tag := range tagNameList {
		if err := db.GetMySQL().Model(&model.User{}).Where("isDelete=0 and tags like ?", "%"+tag+"%").Find(&userList).Error; err != nil {
			log.Warningf("Serch user information fail:%v", err)
			res.ResponseWithError(common.SYSTEM_ERROR, "通过标签查询用户信息出错")
			return res
		}
	}
	var safetyUserList []model.User
	for _, v := range userList {
		safetyUser := utils.GetSafetyUser(&v)
		safetyUserList = append(safetyUserList, safetyUser)
	}
	res.ResponseWithData(safetyUserList)
	return res

}

func UpdateUser(ctx *gin.Context, user, currentUser *model.User) *response.BaseResponse {
	res := &response.BaseResponse{}
	id := user.Id

	// 仅管理员和自己可以修改
	if currentUser == nil || !IsAdmin(ctx) && (id > 0 && id != currentUser.Id) {
		log.Warningf("Non authr")
		res.ResponseWithError(common.NO_AUTH, "无权限")
		return res
	}
	err := db.GetMySQL().Model(&model.User{}).Where("id = ?", id).Limit(1).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		log.Warningf("get user info error")
		res.ResponseWithError(common.NULL_ERROR, "没有该用户")
		return res
	}

	db.GetMySQL().Model(user).Updates(user)
	//查询更新后的数据
	db.GetMySQL().First(user, user.Id)
	setCookie(ctx, user)
	return res
}

func IsAdmin(ctx *gin.Context) bool {
	// 鉴权 仅供管理员查询
	session, err := ctx.Cookie(constant.SessionKey)
	if err != nil {
		return false
	}

	var user model.User
	if err := json.Unmarshal([]byte(session), &user); err != nil {
		return false
	}
	return user.UserRole == constant.AdminRole
}

func MatchUsers(ctx *gin.Context, num int64, currentUser *model.User) *response.BaseResponse {
	return nil
}

// UserRecommend 已缓存
func UserRecommend(ctx *gin.Context, pageSize, pageNum int) *response.BaseResponse {
	if pageSize <= 0 {
		pageSize = 20
	}
	if pageNum <= 0 {
		pageNum = 1
	}
	res := &response.BaseResponse{}

	var userPageData string
	redisKey := fmt.Sprintf("parener:user:recommend:pageNum:%d:pageSize:%d", pageNum , pageSize)
	userPageData, err := db.RedisClient.Get(context.Background(), redisKey).Result()
	if err == nil {
		// have cache
		res.ResponseWithData(userPageData)
		return res
	}

	var userPage []model.User
	// No cache
	if err = db.GetMySQL().Limit(pageSize).Offset((pageNum - 1) * pageSize).Find(&userPage).Error; err != nil {
		res.ResponseWithError(common.SYSTEM_ERROR, "数据查询错误")
		return res
	}
	var safetyUserList []model.User
	for _, v := range userPage {
		safetyUser := utils.GetSafetyUser(&v)
		safetyUserList = append(safetyUserList, safetyUser)
	}
	userPageJson, err := json.Marshal(safetyUserList)
	if err != nil {
		res.ResponseWithError(common.SYSTEM_ERROR, "json 转换错误")
		return res
	}
	userPageData = string(userPageJson)
	// 永不过期
	db.RedisClient.Set(context.Background(), redisKey, userPageData, 0)
	res.ResponseWithData(userPageData)
	return res
}
